package cc.rengu.igas.cops.core.packswitch;

import cc.rengu.igas.cops.common.constant.CopsTreeNodeConstant;
import cc.rengu.igas.cops.common.enums.RespCodeEnum;
import cc.rengu.igas.share.common.constant.ShareParamConstant;
import cc.rengu.igas.share.common.constant.TransCacheConstant;
import cc.rengu.igas.share.core.model.MchntKeyInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.PayOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.PayOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.PayOrderInfo;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.common.enums.TransTypeEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.OrderInfo;
import cc.rengu.oltp.service.model.TxnInfo;
import cc.rengu.oltp.service.realize.OrderService;
import cc.rengu.oltp.service.realize.TxnInfoService;
import cc.rengu.oltp.service.realize.impl.OrderServiceImpl;
import cc.rengu.oltp.service.realize.impl.TxnInfoServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class CopsServerPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {
    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String jsonStr = new String(bytes);
        /* 转换XSS攻击的字符 */
        String signBlockString = generateSignBlockString(jsonStr);
        jsonStr = jsonStr.replaceAll("<", "＜").replaceAll(">", "＞");
        xmlTreeUtil.setXmlTreeStringValue(CopsTreeNodeConstant.SIGNBLOCK, signBlockString);
        return jsonStr.getBytes();
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);
        String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM);

        //退款交易交易码转换
        if (TransTypeEnum.REFUND.equals(TransTypeEnum.getTransType(txnNum))) {
            //先缓存交易请求时的交易码
            GlobalTransUtil.setCacheValue(TransCacheConstant.CACHE_TXN_NUM, txnNum);
            String origUserOrderId = xmlTreeUtil.getXmlTreeStringValue(CopsTreeNodeConstant.ORIG_USER_ORDER_ID);
            OrderService orderService = new OrderServiceImpl();
            OrderInfo origOrderInfo = orderService.selectOrderInfoByUserOrderId(instId, origUserOrderId);
            if (null == origOrderInfo) {
                rglog.error("根据原支付订单的用户订单号<{}>查询订单信息失败!", origUserOrderId);
                return -1;
            }
            PayOrderInfoMapper payOrderInfoMapper = new PayOrderInfoMapperImpl();
            List<PayOrderInfo> origPayOrderInfoList = payOrderInfoMapper.selectPayOrderInfoBySysSeqNum(instId, origOrderInfo.getUserOrderInfo().getSysSeqNum());
            origOrderInfo.setPayOrderInfoList(origPayOrderInfoList);
            GlobalTransUtil.setCacheValue(TransCacheConstant.ORIG_ORDER_INFO, origOrderInfo);
            String origTxnNum = origOrderInfo.getUserOrderInfo().getTxnNum();
            /* 过滤实时入账的支付订单 */
            Predicate<PayOrderInfo> d0Filter1 = p -> !ShareParamConstant.SUB_SYS_ID_D0_REALTIME.equals(p.getChannelBizType());
            Predicate<PayOrderInfo> d0Filter2 = p -> !ShareParamConstant.OTHER_SUB_SYS_ID_D0_REALTIME.equals(p.getChannelBizType());
            origPayOrderInfoList = origPayOrderInfoList.stream().filter(d0Filter1.and(d0Filter2))
                    .sorted(Comparator.comparing(PayOrderInfo::getDstCallIndex)).collect(Collectors.toList());
            /* 获取主体支付方 */
            Predicate<PayOrderInfo> acctFilter = p -> !ChannelEnum.ACCT.getChannelType().equals(p.getMsgDstId());
            Predicate<PayOrderInfo> mompFilter = p -> !ChannelEnum.MOMP.getChannelType().equals(p.getMsgDstId());
            List<PayOrderInfo> origPayOrderInfoListFilter = origPayOrderInfoList.stream().filter(acctFilter.and(mompFilter)).collect(Collectors.toList());
            /* 判断是否存在商户D0实时入账的流水 */
            origPayOrderInfoListFilter = origPayOrderInfoListFilter.stream().filter(item -> !"D0".equals(item.getDstChannelType())).collect(Collectors.toList());
            if (origPayOrderInfoListFilter.size() > 1) {
                rglog.info("当前交易存在多种主支付方式，请排查是否存在主支付方式过滤问题!");
            }
            PayOrderInfo origPayOrderInfo = origPayOrderInfoListFilter.get(0);
            //TODO 待修改未知交易的交易码
            if ("P01CP001".equals(origTxnNum) || "P01CP002".equals(origTxnNum) || "微信APP支付交易码".equals(origTxnNum)) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "P04CP001");
                if ("P01CP001".equals(origTxnNum)) {
                    //微信公众号支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "01");
                } else if ("P01CP002".equals(origTxnNum)) {
                    //微信小程序支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "02");
                } else {
                    //微信APP支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "03");
                }
            } else if ("P01PM003".equals(origTxnNum) || "银联二维码主扫交易码".equals(origTxnNum)) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "P04CP002");
                if (ChannelEnum.UPWX.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.NUWX.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.WXZF.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //微信扫码支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "00");
                } else if (ChannelEnum.UPAL.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.NUAL.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.ALIP.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //支付宝扫码支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "01");
                } else if (ChannelEnum.UPQC.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //银联二维码主扫支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "03");
                }
            } else if ("P01MC006".equals(origTxnNum) || "P01BS002".equals(origTxnNum)) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "P04CP003");
                if (ChannelEnum.UPWX.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.NUWX.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.WXZF.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //微信条码支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "00");
                } else if (ChannelEnum.UPAL.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.NUAL.getChannelType().equals(origPayOrderInfo.getMsgDstId()) ||
                        ChannelEnum.ALIP.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //支付宝条码支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "01");
                } else if (ChannelEnum.UPQC.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //银联二维码被扫支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "03");
                }
            } else if ("P01PM001".equals(origTxnNum) || "P01PM004".equals(origTxnNum) || "P01PM011".equals(origTxnNum) || "P01PM013".equals(origTxnNum)) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "P04CP004");
                if (ChannelEnum.UPAC.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //银联无跳转支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "01");
                } else if (ChannelEnum.NCQS.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //银联无卡快捷支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "02");
                } else {
                    //本行卡快捷支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "00");
                }
            } else if ("P01PM007".equals(origTxnNum) || "P01PM018".equals(origTxnNum)) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "P04CP005");
                if (ChannelEnum.CPCN.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    //中金网关支付退款
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "04");
                }
                if (ChannelEnum.UPGW.getChannelType().equals(origPayOrderInfo.getMsgDstId())) {
                    if ("01".equals(origPayOrderInfo.getBizType())) {
                        //银联新网银企业网关支付退款
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "03");
                    } else {
                        //银联新网银个人网关支付退款
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "02");
                    }
                } else {
                    if ("BB01".equals(origPayOrderInfo.getDstChannelType())) {
                        //本行企业网关支付退款
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "01");
                    } else {
                        //本行个人网关支付退款
                        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, "00");
                    }
                }
            }
        }

        //获取交易配置信息
        TxnInfoService txnInfoService = new TxnInfoServiceImpl();
        TxnInfo txnInfo = txnInfoService.getCurrentTxnInfo();
        xmlTreeUtil.setXmlTreeObjectValue(TreeNodeConstant.TXN_INFO,txnInfo);

        //签名验签
        if (AppParamConstant.YES.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            String signBlockString = xmlTreeUtil.getXmlTreeStringValue(CopsTreeNodeConstant.SIGNBLOCK);
            String signature = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SIGNATURE);
            String platMchntNo = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.PLAT_MCHNT_NO);
            //获取平台商户对应的加签及验签密钥
            if (!StringUtil.isEmptyOrNull(platMchntNo)) {
                MchntService mchntService = new MchntServiceImpl();
                MchntKeyInfo mchntKeyInfo = mchntService.getMchntKeyInfo(instId, platMchntNo, "APPID", "00");
                String signatureKey = mchntKeyInfo.getKeyValue();
                if (StringUtil.isEmptyOrNull(signatureKey)) {
                    rglog.error("平台商户加签验签密钥为空，验签不通过");
                    xmlTreeUtil.setXmlTreeStringValue(CopsTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
                    return 0;
                }
                xmlTreeUtil.setXmlTreeStringValue(CopsTreeNodeConstant.SIGNATURE_KEY, signatureKey);
                if (!MD5Util.verifySignString(signature, signBlockString, "&key=", signatureKey)) {
                    rglog.error("验签失败,signature:<{}>,签名验签密钥:<{}>,待进行签名验签计算的blockString为::<{}>", signature, signatureKey, signBlockString);
                    xmlTreeUtil.setXmlTreeStringValue(CopsTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
                }
            }
        }
        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 获取交易配置信息 */
        TxnInfo txnInfo = (TxnInfo) xmlTreeUtil.getXmlTreeObjectValue(TreeNodeConstant.TXN_INFO, TxnInfo.class);
        if (null == txnInfo) {
            TxnInfoService txnInfoService = new TxnInfoServiceImpl();
            txnInfo = txnInfoService.getCurrentTxnInfo();
        }
        if (AppParamConstant.NO.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            return bytes;
        }
        String jsonBefore = new String(bytes);
        String signBlock = generateSignBlockString(jsonBefore);
        /* 生成生成数字签名的字符串 */
        if (null == signBlock) {
            rglog.error("生成加签Block失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("加签Block:<{}>", signBlock);
        String signatureKey = xmlTreeUtil.getXmlTreeStringValue(CopsTreeNodeConstant.SIGNATURE_KEY);
        String signData = MD5Util.generateSignString(signBlock, "&key=", signatureKey);
        if (null == signData) {
            rglog.error("生成数字签名失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("数字签名:<{}>", signData);

        JSONObject jsonObj = JSON.parseObject(jsonBefore, Feature.OrderedField);
        JSONObject headObj = jsonObj.getJSONObject(CopsTreeNodeConstant.HEADER);
        headObj.put(CopsTreeNodeConstant.SIGN, signData);
        jsonObj.put(CopsTreeNodeConstant.HEADER, headObj);
        rglog.debug("添加signature后发送报文:<{}>", jsonObj.toJSONString());
        return jsonObj.toJSONString().getBytes();
    }

    /**
     * 生成加签Block
     * @param jsonString 原始报文
     * @return 加签BLOCK
     */
    private String generateSignBlockString(String jsonString) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        if (null != jsonString && 0 != jsonString.trim().length()) {
            TreeMap<String, Object> treeMap = StringUtil.jsonStringKeySort(jsonString);
            StringBuilder stringBuffer = new StringBuilder();
            for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
                if ("header".equals(entry.getKey())) {
                    TreeMap<String, String> headerMap = JSON.parseObject(entry.getValue().toString(), TreeMap.class);
                    headerMap.remove("signature");
                    stringBuffer.append(entry.getKey()).append("=").append(JSON.toJSONString(headerMap)).append("&");
                } else {
                    stringBuffer.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
                }
            }
            String signBlockString = stringBuffer.substring(0, stringBuffer.length() - 1);
            rglog.debug("加签字符串:{}", signBlockString);
            return signBlockString;
        } else {
            rglog.error("待生成加签串的字符串为空:{}", jsonString);
            return null;
        }
    }
}
